/*
 * Jiri Cigler
 * ciglej1@fel.cvut.cz 
 *
 * Copyright (C) 2007 Jirka Cigler <ciglej1@fel.cvut.cz>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 */ 



/**
 * \file jobshop.cc
 * \author Jiri Cigler
 * \brief Interface between Matlab and C++ program for computing job
 * shop scheduling problem.
 *
 * =============================================================
 * jobshop.cpp - program for computing job shop problem
 *
 * Computational function that takes 2 arguments
 *  jobshop(resources, processingTime, machines)
 *  
 *  resources ... matrix N x M 
 *  processingTime  ... matrix N x M
 *	where N is number of jobs ( rows of the matrix )
 *	      M is number of tasks in each job ( columns of the matrix )
 * This is a MEX-file for MATLAB.
 * =============================================================
 */

/* $Revision: 1.0 $ */

#include "mex.h"
#include "shopScheduler.hh"

/// Runtime of the searching for optimal solution
double runtime;

/**  cps(shop(PT,P), problem('J2||Cmax'))
 * 

load smallshop; cps(shop(PT,P), problem('J2||Cmax'))

 * \brief Create ShopScheduler and return result
 * \return result
 */
unsigned int** jobshop( int **& resources, unsigned int **& processingTime, int jobs, int tasks , int machines, ShopType stype)
{
	unsigned int **r;
	r = new unsigned int*[jobs];//tasksinjob
	for( int x = 0; x < jobs; x++ )
		r[x] = new unsigned int[tasks];//jobs
	ShopScheduler  * s = new ShopScheduler(false);

//	s->run(ST_OPENSHOP,res ,dur, jobs, tasksInJob, tasksInJob, r, &rtime);

	s->run(stype, resources, processingTime, jobs, tasks,machines, r, &runtime);
/*OUTPUT	
	printf("Solution computed\n"); 
	for(int i=0; i < jobs ; i++){
		for(int j = 0; j<tasks; j++){
			printf("%d ",r[i][j]);
		}
		printf("\n");
	}*/
	return r;
}
/**
 * \brief Convert Matlab data type mxArray to array of arbitrary type.
 * \param a mxArray to be converted
 * \param verbose if verbose !=0 then print data to be converted.
 * \return T array of pointers to specified data type.
 */
template <class T>
T** convertFromMxArray(const mxArray *a, char verbose){
	int rows = mxGetM(a);
	int columns = mxGetN(a);
	T** res;
	res = new T*[rows];
	for( int x = 0; x < rows; x++ )
		res[x] = new T[columns];
	int i, j;
	double * data = mxGetPr(a);
	for(i =0; i < columns; i++){
		for(j =0; j < rows ; j++){
			res[j][i] = (T)*data;
			//if(verbose)
			//	printf(" %f",*data);
			data++;
		}
	}
	/*
	for(i=0; i<rows; i++){
		for(j=0;j<columns;j++){
			printf("%d ",res[i][j]);
		}
		printf("\n");
	}*/
	return res;
}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs,
		const mxArray *prhs[])
{
	int mrows, ncols, jobs, tasksInJob, machines;
//	printf("Welcome to constraint based scheduling\n");
	if(nrhs !=3){
		mexErrMsgTxt("Two inputs required.");
	}
	else if(nlhs!=2){
		mexErrMsgTxt("Too many output arguments");
	}
	else{
		/* The input must be a noncomplex scalar double.*/
		mrows = mxGetM(prhs[1]);
		ncols = mxGetN(prhs[1]);  
		/*if (!mxIsDouble(prhs[2]) || mxIsComplex(prhs[2]) || !(mrows == 1 && ncols == 1)) {
		  mexErrMsgTxt("machines must be a noncomplex scalar double.");
		  }*/
		machines = mrows; 
		//printf("machines %d\n",machines);
		//mxGetPr(prhs[2]);
		//printf("%s\n", mxGetPr(prhs[2]));
		unsigned int ** processingTime;
		int ** resources;
		unsigned int** result;
		//jobshop
		if(*mxGetPr(prhs[2])==1){
			if(mxGetM(prhs[1])!=mxGetM(prhs[0]) || mxGetN(prhs[1])!=mxGetN(prhs[0]) )
				mexErrMsgTxt("Matrixes must have same dimension");
			
			tasksInJob = mxGetN(prhs[0]);

			jobs = mxGetM(prhs[0]);
			plhs[0] = mxCreateDoubleMatrix(jobs,tasksInJob, mxREAL);
			plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL);
			//printf("tasks in job: %d jobs: %d",tasksInJob, jobs);
			processingTime = convertFromMxArray<unsigned int>(prhs[1],1);
			//printf("processingTime converted .. OK\n");
			resources = convertFromMxArray<int>(prhs[0],1);
			//printf("resources converted .. OK\n");
			result = jobshop(resources, processingTime,  jobs,  tasksInJob ,  machines,ST_JOBSHOP);
		}
		else {
			//openshop
			if(*mxGetPr(prhs[2])==2){
				if(mxGetM(prhs[1])!=mxGetM(prhs[0]) || mxGetN(prhs[1])!=mxGetN(prhs[0]) )
					mexErrMsgTxt("Matrixes must have same dimension");
				tasksInJob = mxGetN(prhs[0]);
				jobs = mxGetM(prhs[0]);
				plhs[0] = mxCreateDoubleMatrix(jobs,tasksInJob, mxREAL);
				plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL);
				processingTime = convertFromMxArray<unsigned int>(prhs[1],1);
				//printf("processingTime converted .. OK\n");
				resources = convertFromMxArray<int>(prhs[0],1);
				//printf("resources converted .. OK\n");
				result = jobshop(resources, processingTime,  jobs,  tasksInJob ,  machines,ST_OPENSHOP);
			}
			else{ 
				//flowshop
				if(*mxGetPr(prhs[2])==3){
					tasksInJob = mxGetN(prhs[1]);
					jobs = mxGetM(prhs[1]);
					plhs[0] = mxCreateDoubleMatrix(jobs,tasksInJob, mxREAL);
					plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL);
					processingTime = convertFromMxArray<unsigned int>(prhs[1],1);
					//printf("processingTime converted .. OK\n");
					resources = NULL;
					result = jobshop(resources, processingTime,  jobs,  tasksInJob ,  machines,ST_FLOWSHOP);

				}
				else{
					//printf("Unknown parameter try 1-3 \n")	;
				}
			}
		}

		//printf("result\n");


		double *data = mxGetPr(plhs[0]);
		//printf("Pointer to result\n");
		for(int i=0; i < tasksInJob ; i++){
			for(int j=0; j < jobs; j++){
				*data = result[j][i];
				data++;
			}
		}
		//printf("result converted .. OK\n");
		data = mxGetPr(plhs[1]);
		*data = runtime;

		//printf("Preparing clean-up \n");
		for (int i=0; i<jobs; i++){
			if (result[i]!=NULL)
				delete result[i];
			if (processingTime[i]!=NULL)
				delete processingTime[i];
			if(resources !=NULL)
				if (resources[i]!=NULL)
					delete resources[i];
		}
		if (result!=NULL)
			delete result;
		if (processingTime!=NULL)
			delete processingTime;
		if(resources!=NULL)
			delete resources;
		//printf("Clean-up OK\n");
	//	double * d = mxGetPr(plhs[1]);
	}
};

/**
 *\mainpage JC SHOP
 * \section abstract Abstract:
 *  The main aim of this work is to create small program based on constraint programming for solving shop scheduling problems e.q Job-shop, Flow-shop, Open-shop	
 * \section intro Introduction:
 * Scheduling problems are combinatorial and optimizing problems (often
 * NP-hard) and thus can be described by constraints -> constraint base
 * scheduling is good approach for solving mentioned problems.
 * We use gecode (www.gecode.org) constraint programming library.
 * Benefits of this library are: open-source, speed 
 * Disadvantages are: it is still in the progress - lack of
 * documentation.
 *
 * \section install Installation:
 * 	- Install gecode library (http://www.gecode.org/download.html)
 *	- Download sources:
 * 	 (http://code.google.com/p/jcjobshop/downloads/list)
 * 	 or checkout from svn repository
 * 	 svn checkout http://jcjobshop.googlecode.com/svn/trunk/ jcjobshop
 * 	- Compile sources (make)
 * 	- Copy bin/shop.mexglx to location of Matlab path
 * 	- enjoy
 *
 * \section usage Usage:
 * Usage from Matlab is very simple. 
 * [StartTimes, runtime] = shop(R,P,ShopType)
 * R ... matrix of resources
 * P ... matrix of processing times
 * ShopType:
 * 	- "1" Job-shop
 * 	- "2" Open-shop
 * 	- "3" Flow-shop
 * StartTimes ... matrix containing start times of each task in each
 * jobs.
 * runtime ... solving time [ms]
 * 
 */

